using System;
using System.Collections.Generic;
using System.Linq;
using Lync.Widget.Library.Caching;
using Lync.Widget.Library.Configuration;
using Lync.Widget.Library.Extensions;
using Lync.Widget.Library.UserService;

namespace Lync.Widget.Library.Repositories
{
    public sealed class ActiveDirectoryCachedRepository : IActiveDirectoryRepository
    {
        #region Private FIelds

        private readonly IUserServiceConfiguration m_UserServiceConfiguration;
        private readonly ICacheManager m_CacheManager;
        private readonly IActiveDirectoryRepository m_Repository;
        

        #endregion

        private UserCacheDictionary m_CacheDictionary;
        private UserCacheDictionary UserCacheDictionary
        {
            get { return GetOrCreateCacheUserCache(); }
        }

        private ActiveDirectoryGroupCacheDictionary m_ActiveDirectoryGroupCacheDictionary;
        private ActiveDirectoryGroupCacheDictionary ActiveDirectoryGroupCacheDictionary
        {
            get { return GetOrCreateCacheADGroupCache(); }
        }

        private GroupCacheDictionary m_GroupCacheDictionary;
        private GroupCacheDictionary GroupCacheDictionary
        {
            get { return GetOrCreateCacheGroupCache(); }
        }


        #region Constructor

        public ActiveDirectoryCachedRepository()
            : this(new ActiveDirectoryRepository(),
                   new UserServiceConfiguration(),
                   new CacheManager())
        { }

        public ActiveDirectoryCachedRepository(IActiveDirectoryRepository repository,
                                               IUserServiceConfiguration userServiceConfiguration,
                                               ICacheManager cacheManager)
        {
            m_Repository = repository;
            m_UserServiceConfiguration = userServiceConfiguration;
            m_CacheManager = cacheManager;
        }

        #endregion

        #region Public Methods

        public IList<User> GetUsers(User user, char separator = ',')
        {
            if (user == null) throw new ArgumentNullException("user");

            var cacheKey = string.Format("{0}{1}", user.Serialize(), separator);

            if (!UserCacheDictionary.Exists(cacheKey))
            {
                var temp = m_Repository.GetUsers(user, separator);
                UserCacheDictionary.Add(cacheKey, temp);    
            }

            return UserCacheDictionary.Get(cacheKey);
        }
      
        public IList<User> GetUsers(string query, char separator = '+')
        {
            if (query == null) throw new ArgumentNullException("query");

            var cacheKey = String.Format("{0}{1}", query, separator);

            if (!UserCacheDictionary.Exists(cacheKey))
            {
                var temp = m_Repository.GetUsers(query, separator);
                UserCacheDictionary.Add(cacheKey, temp);
            }

            return UserCacheDictionary.Get(cacheKey);
        }

        public IList<IActiveDirectoryGroup> GetGroupsFromConfiguration()
        {
            const string cacheKey = "GroupsFromConfiguration";

            if (!ActiveDirectoryGroupCacheDictionary.Exists(cacheKey))
            {
                var temp = m_Repository.GetGroupsFromConfiguration();
                ActiveDirectoryGroupCacheDictionary.Add(cacheKey, temp);
            }

            return ActiveDirectoryGroupCacheDictionary.Get(cacheKey);
        }

        public IActiveDirectoryGroup GetGroupFromConfiguration(string groupId)
        {            
            var groups = GetGroupsFromConfiguration();

            return groups.SingleOrDefault(group => @group.Id == groupId);
        }

        public IList<Group> GetGroups()
        {
            const string cacheKey = "Groups";

            if (!GroupCacheDictionary.Exists(cacheKey))
            {
                var temp = m_Repository.GetGroups();
                GroupCacheDictionary.Add(cacheKey, temp);
            }

            return GroupCacheDictionary.Get(cacheKey);
        }

        public IList<Group> GetGroups(string organisationUnit)
        {
            var cacheKey = String.Format("Groups_{0}", organisationUnit);

            if (!GroupCacheDictionary.Exists(cacheKey))
            {
                var temp = m_Repository.GetGroups(organisationUnit);
                GroupCacheDictionary.Add(cacheKey, temp);
            }

            return GroupCacheDictionary.Get(cacheKey);
        }

        public IList<User> GetUsersFromGroup(Group group)
        {
            var cacheKey = String.Format("GroupMembers_{0}_{1}", group.Name, group.DistinguishedName);

            if (!UserCacheDictionary.Exists(cacheKey))
            {
                var temp = m_Repository.GetUsersFromGroup(group);
                UserCacheDictionary.Add(cacheKey, temp);
            }

            return UserCacheDictionary.Get(cacheKey);
        }

        public User GetUser(string sipUri, char separator = '+')
        {
            if (String.IsNullOrEmpty(sipUri)) return null;

            const string cacheKey = "AllUsers";

            if (!UserCacheDictionary.Exists(cacheKey))
            {
                var temp = new List<User>();
                UserCacheDictionary.Add(cacheKey, temp);
            }

            var cached = UserCacheDictionary.Get(cacheKey);

            if (cached.All(t => t.SipAddress != sipUri))
            {
                var user = m_Repository.GetUser(sipUri);

                if (user != null)
                {
                    cached.Add(user);
                }
            }

            return cached.SingleOrDefault(t => t.SipAddress == sipUri);
        }

        public IList<User> GetUsers(List<string> sipUri, char separator = '+')
        {
            return sipUri.Select(sip => GetUser(sip, separator)).Where(u => u != null).ToList();
        }

        #endregion


        private UserCacheDictionary GetOrCreateCacheUserCache()
        {
            if (m_UserServiceConfiguration.CacheMinutes > 0)
            {
                var cacheDictionary = m_CacheManager.Get<UserCacheDictionary>("UserCacheDictionary");

                if (cacheDictionary == null)
                {
                    cacheDictionary = new UserCacheDictionary();
                    m_CacheManager.Add("UserCacheDictionary", cacheDictionary, m_UserServiceConfiguration.CacheMinutes);
                }
                return cacheDictionary;
            }

            return m_CacheDictionary ?? (m_CacheDictionary = new UserCacheDictionary());
        }

        private ActiveDirectoryGroupCacheDictionary GetOrCreateCacheADGroupCache()
        {
            if (m_UserServiceConfiguration.CacheMinutes > 0)
            {
                var cacheDictionary = m_CacheManager.Get<ActiveDirectoryGroupCacheDictionary>("ActiveDirectoryGroupCacheDictionary");

                if (cacheDictionary == null)
                {
                    cacheDictionary = new ActiveDirectoryGroupCacheDictionary();
                    m_CacheManager.Add("ActiveDirectoryGroupCacheDictionary", cacheDictionary, m_UserServiceConfiguration.CacheMinutes);
                }
                return cacheDictionary;
            }

            return m_ActiveDirectoryGroupCacheDictionary ?? (m_ActiveDirectoryGroupCacheDictionary = new ActiveDirectoryGroupCacheDictionary());
        }

        private GroupCacheDictionary GetOrCreateCacheGroupCache()
        {
            if (m_UserServiceConfiguration.CacheMinutes > 0)
            {
                var cacheDictionary = m_CacheManager.Get<GroupCacheDictionary>("GroupCacheDictionary");

                if (cacheDictionary == null)
                {
                    cacheDictionary = new GroupCacheDictionary();
                    m_CacheManager.Add("GroupCacheDictionary", cacheDictionary, m_UserServiceConfiguration.CacheMinutes);
                }
                return cacheDictionary;
            }

            return m_GroupCacheDictionary ?? (m_GroupCacheDictionary = new GroupCacheDictionary());
        }

    }
}